import _ from "lodash";
import axios, {AxiosInstance, AxiosRequestConfig, AxiosResponse} from "axios";
import {MockConfig, RequestConfig,} from "./http-client-type.js";
import {defaultCustomRequestConfig, defaultMockConfig} from "./http-client-default-config.js";
import handleRequestFulfilled from "./handle-request-fulfilled.js";
import handleResponseFulfilled from "./handle-response-fulfilled.js";
import handleResponseRejected from "./handle-response-rejected.js";
import HttpClientUtil from "./http-client-util.js";

export default class HttpClient {
  axiosInstance: AxiosInstance;

  requestConfig: RequestConfig<any>;

  mockConfig: MockConfig;

  constructor(
      requestConfig: RequestConfig<any>,
      mockConfig?: MockConfig
  ) {
    this.requestConfig = _.defaultsDeep(
        requestConfig,
        defaultCustomRequestConfig
    );
    this.mockConfig = mockConfig
        ? _.defaultsDeep(mockConfig, defaultMockConfig)
        : defaultMockConfig;

    this.axiosInstance = axios.create(this.requestConfig);

    // 请求拦截器
    this.axiosInstance.interceptors.request.use(
        handleRequestFulfilled.bind(this)
    );
    // 响应拦截器
    this.axiosInstance.interceptors.response.use(
        handleResponseFulfilled.bind(this),
        handleResponseRejected.bind(this)
    );
  }

  // ####################### API #####################

  // 请求方法
  request<T = any, R = AxiosResponse<T>, D = any>(
      config: RequestConfig<D>,
  ): Promise<R> {
    if (HttpClientUtil.isTemplateMock(config, this.mockConfig)) {
      return HttpClientUtil.mockTemplateResponse(config, this);
    }

    return this.axiosInstance.request(config as AxiosRequestConfig);
  }

  get<T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      config?: RequestConfig<D>,
  ): Promise<R> {
    if (HttpClientUtil.isTemplateMock(config, this.mockConfig)) {
      return HttpClientUtil.mockTemplateResponse(config, this);
    }

    return this.axiosInstance.get(url, config as AxiosRequestConfig);
  }

  delete<T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      config?: RequestConfig<D>,
  ): Promise<R> {
    if (HttpClientUtil.isTemplateMock(config, this.mockConfig)) {
      return HttpClientUtil.mockTemplateResponse(config, this);
    }

    return this.axiosInstance.delete(url, config as AxiosRequestConfig);
  }

  head<T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      config?: RequestConfig<D>,
  ): Promise<R> {
    if (HttpClientUtil.isTemplateMock(config, this.mockConfig)) {
      return HttpClientUtil.mockTemplateResponse(config, this);
    }

    return this.axiosInstance.head(url, config as AxiosRequestConfig);
  }

  options<T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      config?: RequestConfig<D>,
  ): Promise<R> {
    if (HttpClientUtil.isTemplateMock(config, this.mockConfig)) {
      return HttpClientUtil.mockTemplateResponse(config, this);
    }

    return this.axiosInstance.options(url, config as AxiosRequestConfig);
  }

  post<T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      data?: D,
      config?: RequestConfig<D>,
  ): Promise<R> {
    if (HttpClientUtil.isTemplateMock(config, this.mockConfig)) {
      return HttpClientUtil.mockTemplateResponse(config, this);
    }

    return this.axiosInstance.post(url, data, config as AxiosRequestConfig);
  }

  put<T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      data?: D,
      config?: RequestConfig<D>,
  ): Promise<R> {
    if (HttpClientUtil.isTemplateMock(config, this.mockConfig)) {
      return HttpClientUtil.mockTemplateResponse(config, this);
    }

    return this.axiosInstance.put(url, data, config as AxiosRequestConfig);
  }

  patch<T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      data?: D,
      config?: RequestConfig<D>,
  ): Promise<R> {
    if (HttpClientUtil.isTemplateMock(config, this.mockConfig)) {
      return HttpClientUtil.mockTemplateResponse(config, this);
    }

    return this.axiosInstance.patch(url, data, config as AxiosRequestConfig);
  }

  postForm<T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      data?: D,
      config?: RequestConfig<D>,
  ): Promise<R> {
    if (HttpClientUtil.isTemplateMock(config, this.mockConfig)) {
      return HttpClientUtil.mockTemplateResponse(config, this);
    }

    return this.axiosInstance.postForm(url, data, config as AxiosRequestConfig);
  }

  putForm<T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      data?: D,
      config?: RequestConfig<D>,
  ): Promise<R> {
    if (HttpClientUtil.isTemplateMock(config, this.mockConfig)) {
      return HttpClientUtil.mockTemplateResponse(config, this);
    }

    return this.axiosInstance.putForm(url, data, config as AxiosRequestConfig);
  }

  patchForm<T = any, R = AxiosResponse<T>, D = any>(
      url: string,
      data?: D,
      config?: RequestConfig<D>,
  ): Promise<R> {
    if (HttpClientUtil.isTemplateMock(config, this.mockConfig)) {
      return HttpClientUtil.mockTemplateResponse(config, this);
    }

    return this.axiosInstance.patchForm(
        url,
        data,
        config as AxiosRequestConfig,
    );
  }
}
